﻿using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using fCloud.SensorApi.Models;
using Microsoft.Extensions.Caching.Distributed;
using StackExchange.Redis;

namespace fCloud.SensorApi.Services;

public class RedisService(IConnectionMultiplexer redisConnection,ILogger<RedisService> logger) : IRedisService
{
    private readonly IDatabase _redisDb = redisConnection.GetDatabase();
    
    [Obsolete("Obsolete")]
    private bool IsConnected => 
        _redisDb.Multiplexer.GetServers().Any(s => s is { IsConnected: true, IsSlave: false });

    public async Task<RedisValue> GetValueAsync(string key)
    {
        //logger.LogInformation();
        return await _redisDb.StringGetAsync(key);
    }

    public async Task SetValueAsync(string key, string value)
    {
        logger.LogInformation("写入缓存：${key}-{value}",key,value);
        await _redisDb.StringSetAsync(key,value);
    }

    public async Task SetHashValueAsync(string key,string field,string value,TimeSpan? expireTime=null)
    {
        logger.LogInformation("写入Hash缓存，Key:{CacheKey}, Field:{FieldName}, Value:{CacheValue}", 
            key, field, value);
        await _redisDb.HashSetAsync(key, field, value);
        await _redisDb.KeyExpireAsync(key, expireTime);
    }

    [Obsolete("Obsolete")]
    private async Task SetHashEntryAsync(string key,HashEntry[] hashEntries, TimeSpan? expireTime=null)
    {
        if (!IsConnected)
        {
            logger.LogError("Redis连接失败");
        }

        try
        {
            // 显式等待操作完成
            await _redisDb.HashSetAsync(key, hashEntries).ConfigureAwait(false);
        
            if (expireTime.HasValue)
            {
                var expireSuccess = await _redisDb.KeyExpireAsync(key, expireTime);
                logger.LogDebug("Key过期设置结果：{Success}", expireSuccess);
            }
        
            logger.LogInformation("哈希数据操作完成 Key:{Key}", key);
        }
        catch (Exception ex) when (ex is RedisException or TimeoutException)
        {
            logger.LogError(ex, "Redis操作失败 Key:{Key}", key);
            //throw new InvalidOperationException("Redis操作异常", ex);
        }
    }

    public async Task<RedisValue> GetHashValueAsync(string key, string field)
    {
        return await _redisDb.HashGetAsync(key,field);
    }

    [Obsolete("Obsolete")]
    public async Task InsertSensorDataAsync(SensorData sensorData)
    {
        var key = $"realtime:sensor:{sensorData.SensorSn}";
        var hasEntries = ConvertToHashEntries(sensorData);
        await SetHashEntryAsync(key, hasEntries, TimeSpan.FromSeconds(600));
    }
    
    private static SensorData ConvertHashEntriesToSensorData(HashEntry[] entries, string sensorSn)
    {
        var data = new SensorData { SensorSn = sensorSn };
        var properties = typeof(SensorData).GetProperties()
            .Where(p => p.Name != nameof(SensorData.SensorSn))
            .ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);

        foreach (var entry in entries)
        {
            if (!properties.TryGetValue(entry.Name, out var prop)) continue;
            var valueStr = entry.Value.ToString();
            object? value = null;

            if (prop.PropertyType == typeof(Guid))
            {
                // 特殊处理 Guid 类型
                if (Guid.TryParse(valueStr, out var guidValue))
                {
                    value = guidValue;
                }
                else
                {
                    // 可选：记录日志或跳过无效 Guid
                    continue;
                }
            }
            else if (prop.PropertyType.IsEnum)
            {
                // 处理枚举类型
                try
                {
                    value = Enum.Parse(prop.PropertyType, valueStr, ignoreCase: true);
                }
                catch
                {
                    continue;
                }
            }
            else if (prop.PropertyType == typeof(DateTime))
            {
                // 已有 DateTime 格式处理
                value = DateTime.ParseExact(valueStr, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
            }
            else
            {
                // 基础类型、字符串等通用转换
                value = Convert.ChangeType(valueStr, prop.PropertyType, CultureInfo.InvariantCulture);
            }

            prop.SetValue(data, value);
        }

        return data;
    }
    private static string TryParseSensorSn(
        RedisKey redisKey, 
        string prefix)
    {
        var keyString = redisKey.ToString();
        var sensorSn = keyString.AsSpan(prefix.Length).ToString();
        return sensorSn;
    }
    public static string GetRequiredValue(HashEntry[] entries, string fieldName)
    {
        var entry = entries.FirstOrDefault(x => x.Name == fieldName);
        if (entry.Equals(default))
            throw new KeyNotFoundException($"缺失必要字段: {fieldName}");
            
        return entry.Value.ToString();
    }
    

    public async Task<List<SensorData>> ScanSensorKeysAsync()
    {
        const string pattern = "realtime:sensor:*";
        var sensorList = new List<SensorData>();
        var endpoints = _redisDb.Multiplexer.GetEndPoints();

        foreach (var endpoint in endpoints)
        {
            var server = _redisDb.Multiplexer.GetServer(endpoint);
        
            if (!server.IsConnected)
            {
                logger.LogWarning("Redis节点 {Endpoint} 未连接", endpoint);
                continue;
            }
            try
            {
                await foreach (var redisKey in server.KeysAsync(
                                   database: _redisDb.Database,  // 使用当前数据库
                                   pattern: pattern,
                                   pageSize: 6))
                {
                    var hashEntries =  _redisDb.HashGetAll(redisKey);

                    var sensorNew =
                        ConvertHashEntriesToSensorData(hashEntries, TryParseSensorSn(redisKey, "realtime:sensor:"));
                    sensorList.Add(sensorNew);
                }
            }
            catch (RedisServerException ex) when (ex.Message.Contains("NOAUTH"))
            {
                logger.LogCritical("缺少管理员权限，请检查连接字符串是否包含 allowAdmin=true");
            }
        }

        return sensorList;

    }
    private static HashEntry[] ConvertToHashEntries(SensorData sensorData)
    {
        var entries = new List<HashEntry>();
        var properties = typeof(SensorData).GetProperties();

        foreach (var prop in properties)
        {
            var value = prop.GetValue(sensorData);
            if (value == null) continue;
            var stringValue = prop.PropertyType == typeof(DateTime)
                ? ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss")
                : Convert.ToString(value, CultureInfo.InvariantCulture);

            entries.Add(new HashEntry(prop.Name, stringValue));
        }

        return entries.ToArray();
    }

}

public interface IRedisService
{
    public Task<RedisValue> GetValueAsync(string key);
    public Task SetValueAsync(string key, string value);
    public Task SetHashValueAsync(string key, string field, string value, TimeSpan? expireTime = null);
    public Task<RedisValue> GetHashValueAsync(string key, string field);
    public Task InsertSensorDataAsync(SensorData sensorData);
    public Task<List<SensorData>> ScanSensorKeysAsync();
}